return $output;
}
+ /**
+ * @inheritdoc
+ */
+ public function isSelected( FormOptions $opts ) {
+ return !$opts[ $this->getName() ] &&
+ array_filter( $this->getSiblings(), function ( $sibling ) use ( $opts ) {
+ return $opts[ $sibling->getName() ];
+ } );
+ }
}
if ( $other instanceof ChangesListFilterGroup ) {
$this->conflictingGroups[] = [
'group' => $other->getName(),
+ 'groupObject' => $other,
'globalDescription' => $globalDescription,
'contextDescription' => $contextDescription,
];
$this->conflictingFilters[] = [
'group' => $other->getGroup()->getName(),
'filter' => $other->getName(),
+ 'filterObject' => $other,
'globalDescription' => $globalDescription,
'contextDescription' => $contextDescription,
];
);
foreach ( $conflicts as $conflictInfo ) {
+ unset( $conflictInfo['filterObject'] );
+ unset( $conflictInfo['groupObject'] );
$output['conflicts'][] = $conflictInfo;
array_push(
$output['messageKeys'],
return $output;
}
+
+ /**
+ * Checks whether this filter is selected in the provided options
+ *
+ * @param FormOptions $opts
+ * @return bool
+ */
+ abstract public function isSelected( FormOptions $opts );
+
+ /**
+ * Get groups conflicting with this filter
+ *
+ * @return ChangesListFilterGroup[]
+ */
+ public function getConflictingGroups() {
+ return array_map(
+ function ( $conflictDesc ) {
+ return $conflictDesc[ 'groupObject' ];
+ },
+ $this->conflictingGroups
+ );
+ }
+
+ /**
+ * Get filters conflicting with this filter
+ *
+ * @return ChangesListFilter[]
+ */
+ public function getConflictingFilters() {
+ return array_map(
+ function ( $conflictDesc ) {
+ return $conflictDesc[ 'filterObject' ];
+ },
+ $this->conflictingFilters
+ );
+ }
+
+ /**
+ * Check if the conflict with a group is currently "active"
+ *
+ * @param ChangesListFilterGroup $group
+ * @param FormOptions $opts
+ * @return bool
+ */
+ public function activelyInConflictWithGroup( ChangesListFilterGroup $group, FormOptions $opts ) {
+ if ( $group->anySelected( $opts ) && $this->isSelected( $opts ) ) {
+ /** @var ChangesListFilter $siblingFilter */
+ foreach ( $this->getSiblings() as $siblingFilter ) {
+ if ( $siblingFilter->isSelected( $opts ) && !$siblingFilter->hasConflictWithGroup( $group ) ) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private function hasConflictWithGroup( ChangesListFilterGroup $group ) {
+ return in_array( $group, $this->getConflictingGroups() );
+ }
+
+ /**
+ * Check if the conflict with a filter is currently "active"
+ *
+ * @param ChangesListFilter $filter
+ * @param FormOptions $opts
+ * @return bool
+ */
+ public function activelyInConflictWithFilter( ChangeslistFilter $filter, FormOptions $opts ) {
+ if ( $this->isSelected( $opts ) && $filter->isSelected( $opts ) ) {
+ /** @var ChangesListFilter $siblingFilter */
+ foreach ( $this->getSiblings() as $siblingFilter ) {
+ if (
+ $siblingFilter->isSelected( $opts ) &&
+ !$siblingFilter->hasConflictWithFilter( $filter )
+ ) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private function hasConflictWithFilter( ChangeslistFilter $filter ) {
+ return in_array( $filter, $this->getConflictingFilters() );
+ }
+
+ /**
+ * Get filters in the same group
+ *
+ * @return ChangesListFilter[]
+ */
+ protected function getSiblings() {
+ return array_filter(
+ $this->getGroup()->getFilters(),
+ function ( $filter ) {
+ return $filter !== $this;
+ }
+ );
+ }
}
if ( $other instanceof ChangesListFilterGroup ) {
$this->conflictingGroups[] = [
'group' => $other->getName(),
+ 'groupObject' => $other,
'globalDescription' => $globalDescription,
'contextDescription' => $contextDescription,
];
$this->conflictingFilters[] = [
'group' => $other->getGroup()->getName(),
'filter' => $other->getName(),
+ 'filterObject' => $other,
'globalDescription' => $globalDescription,
'contextDescription' => $contextDescription,
];
foreach ( $conflicts as $conflictInfo ) {
$output['conflicts'][] = $conflictInfo;
+ unset( $conflictInfo['filterObject'] );
+ unset( $conflictInfo['groupObject'] );
array_push(
$output['messageKeys'],
$conflictInfo['globalDescription'],
return $output;
}
+
+ /**
+ * Get groups conflicting with this filter group
+ *
+ * @return ChangesListFilterGroup[]
+ */
+ public function getConflictingGroups() {
+ return array_map(
+ function ( $conflictDesc ) {
+ return $conflictDesc[ 'groupObject' ];
+ },
+ $this->conflictingGroups
+ );
+ }
+
+ /**
+ * Get filters conflicting with this filter group
+ *
+ * @return ChangesListFilter[]
+ */
+ public function getConflictingFilters() {
+ return array_map(
+ function ( $conflictDesc ) {
+ return $conflictDesc[ 'filterObject' ];
+ },
+ $this->conflictingFilters
+ );
+ }
+
+ /**
+ * Check if any filter in this group is selected
+ *
+ * @param FormOptions $opts
+ * @return bool
+ */
+ public function anySelected( FormOptions $opts ) {
+ return !!count( array_filter(
+ $this->getFilters(),
+ function ( ChangesListFilter $filter ) use ( $opts ) {
+ return $filter->isSelected( $opts );
+ }
+ ) );
+ }
}
public function displaysOnUnstructuredUi() {
return false;
}
+
+ /**
+ * @inheritdoc
+ */
+ public function isSelected( FormOptions $opts ) {
+ $values = explode(
+ ChangesListStringOptionsFilterGroup::SEPARATOR,
+ $opts[ $this->getGroup()->getName() ]
+ );
+ return in_array( $this->getName(), $values );
+ }
}
];
}
+ /**
+ * Check if filters are in conflict and guaranteed to return no results.
+ *
+ * @return bool
+ */
+ protected function areFiltersInConflict() {
+ $opts = $this->getOptions();
+ /** @var ChangesListFilterGroup $group */
+ foreach ( $this->getFilterGroups() as $group ) {
+
+ if ( $group->getConflictingGroups() ) {
+ wfLogWarning(
+ $group->getName() .
+ " specifies conflicts with other groups but these are not supported yet."
+ );
+ }
+
+ /** @var ChangesListFilter $conflictingFilter */
+ foreach ( $group->getConflictingFilters() as $conflictingFilter ) {
+ if ( $conflictingFilter->activelyInConflictWithGroup( $group, $opts ) ) {
+ return true;
+ }
+ }
+
+ /** @var ChangesListFilter $filter */
+ foreach ( $group->getFilters() as $filter ) {
+
+ /** @var ChangesListFilter $conflictingFilter */
+ foreach ( $filter->getConflictingFilters() as $conflictingFilter ) {
+ if (
+ $conflictingFilter->activelyInConflictWithFilter( $filter, $opts ) &&
+ $filter->activelyInConflictWithFilter( $conflictingFilter, $opts )
+ ) {
+ return true;
+ }
+ }
+
+ }
+
+ }
+
+ return false;
+ }
+
/**
* Main execution point
*
return false;
}
+ if ( $this->areFiltersInConflict() ) {
+ return false;
+ }
+
// array_merge() is used intentionally here so that hooks can, should
// they so desire, override the ORDER BY / LIMIT condition(s); prior to
// MediaWiki 1.26 this used to use the plus operator instead, which meant
],
];
}
+
+ public function provideGetFilterConflicts() {
+ return [
+ [
+ "parameters" => [],
+ "expectedConflicts" => false,
+ ],
+ [
+ "parameters" => [
+ "hideliu" => true,
+ "userExpLevel" => "newcomer",
+ ],
+ "expectedConflicts" => true,
+ ],
+ [
+ "parameters" => [
+ "hideanons" => true,
+ "userExpLevel" => "learner",
+ ],
+ "expectedConflicts" => false,
+ ],
+ [
+ "parameters" => [
+ "hidemajor" => true,
+ "hidenewpages" => true,
+ "hidepageedits" => true,
+ "hidecategorization" => false,
+ "hidelog" => true,
+ "hideWikidata" => true,
+ ],
+ "expectedConflicts" => true,
+ ],
+ [
+ "parameters" => [
+ "hidemajor" => true,
+ "hidenewpages" => false,
+ "hidepageedits" => true,
+ "hidecategorization" => false,
+ "hidelog" => false,
+ "hideWikidata" => true,
+ ],
+ "expectedConflicts" => true,
+ ],
+ [
+ "parameters" => [
+ "hidemajor" => true,
+ "hidenewpages" => false,
+ "hidepageedits" => false,
+ "hidecategorization" => true,
+ "hidelog" => true,
+ "hideWikidata" => true,
+ ],
+ "expectedConflicts" => false,
+ ],
+ [
+ "parameters" => [
+ "hideminor" => true,
+ "hidenewpages" => true,
+ "hidepageedits" => true,
+ "hidecategorization" => false,
+ "hidelog" => true,
+ "hideWikidata" => true,
+ ],
+ "expectedConflicts" => false,
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideGetFilterConflicts
+ */
+ public function testGetFilterConflicts( $parameters, $expectedConflicts ) {
+ $context = new RequestContext;
+ $context->setRequest( new FauxRequest( $parameters ) );
+ $this->changesListSpecialPage->setContext( $context );
+
+ $this->assertEquals(
+ $expectedConflicts,
+ $this->changesListSpecialPage->areFiltersInConflict()
+ );
+ }
}
'instead of testing the abstract class'
);
}
+
+ public function isSelected( FormOptions $opts ) {
+ return false;
+ }
}